#pragma once
#include "ComHeader.h"

class BezierCell;
class Point {
public:
	Point() :m_x(0), m_y(0), m_z(0), m_w(1.) {

	}
	Point(double x, double y, double z) :m_x(x), m_y(y), m_z(z), m_w(1.) {

	}
	Point(double x, double y, double z, double w) :m_x(x), m_y(y), m_z(z), m_w(w) {

	}
	//Point(Eigen::Vector3d vec) :m_x(vec.x()), m_y(vec.y()), m_z(vec.z()), m_w(1.) {

	//}
	Point(Eigen::Vector3f vec) :m_x(vec.x()), m_y(vec.y()), m_z(vec.z()), m_w(1.) {

	}
	double& operator[](const int index) {
		switch (index) {
		case 0:
			return m_x;
		case 1:
			return m_y;
		case 2:
			return m_z;
		default:
			return m_w;
		}
	}
	Point operator* (const double num) {
		m_x *= num;
		m_y *= num;
		m_z *= num;
		m_w *= num;
		return *this;
	}

	Point operator*= (const double num) {
		m_x *= num;
		m_y *= num;
		m_z *= num;
		m_w *= num;
		return *this;
	}

	Point operator/ (const double num) {
		assert(int(num) != 0);
		m_x /= num;
		m_y /= num;
		m_z /= num;
		m_w /= num;
		return *this;
	}

	Point operator/= (const double num) {
		assert(int(num) != 0);
		m_x /= num;
		m_y /= num;
		m_z /= num;
		m_w /= num;
		return *this;
	}

	Point operator+ (const Point point) {
		m_x += point.m_x;
		m_y += point.m_y;
		m_z += point.m_z;
		m_w += point.m_w;
		return *this;
	}

	Point operator+ (const Eigen::Vector3f vc) {
		m_x += vc.x();
		m_y += vc.y();
		m_z += vc.z();
		return *this;
	}

	Point operator+= (const Point point) {
		m_x += point.m_x;
		m_y += point.m_y;
		m_z += point.m_z;
		m_w += point.m_w;
		return *this;
	}
	bool operator> (const Point point) {
		return m_x > point.m_x&&
		m_y > point.m_y&&
		m_z > point.m_z;

	}
	Point projectPoint() {
		return Point(m_x / m_w, m_y / m_w, m_z / m_w);
	}

	double distance(Point point) {
		return std::sqrt(std::pow(m_x - point.m_x, 2) + std::pow(m_y - point.m_y, 2) + std::pow(m_z - point.m_z, 2));
	}

	double m_x, m_y, m_z, m_w;
};
class Vector3f {
public:
	double x, y, z;

	Vector3f(Point point) : x(point.m_x), y(point.m_y), z(point.m_z) {}
	Vector3f(Eigen::Vector3d vc3d) : x(vc3d.x()), y(vc3d.y()), z(vc3d.z()) {}
	Vector3f() : x(0), y(0), z(0) {}
	Vector3f(float xx) : x(xx), y(xx), z(xx) {}
	Vector3f(float xx, float yy, float zz) : x(xx), y(yy), z(zz) {}
	Vector3f operator * (const float& r) const { return Vector3f(x * r, y * r, z * r); }
	Vector3f operator / (const float& r) const { return Vector3f(x / r, y / r, z / r); }

	Vector3f operator * (const Vector3f& v) const { return Vector3f(x * v.x, y * v.y, z * v.z); }
	Vector3f operator - (const Vector3f& v) const { return Vector3f(x - v.x, y - v.y, z - v.z); }
	Vector3f operator + (const Vector3f& v) const { return Vector3f(x + v.x, y + v.y, z + v.z); }
	Vector3f operator - () const { return Vector3f(-x, -y, -z); }
	Vector3f& operator += (const Vector3f& v) { x += v.x, y += v.y, z += v.z; return *this; }
	friend Vector3f operator * (const float& r, const Vector3f& v)
	{
		return Vector3f(v.x * r, v.y * r, v.z * r);
	}
	friend std::ostream& operator << (std::ostream& os, const Vector3f& v)
	{
		return os << v.x << ", " << v.y << ", " << v.z;
	}
	double operator[](int index) const{
		switch (index) {
		case 0:
			return this->x;
		case 1:
			return this->y;
		case 2:
			return this->z;

		}
	}
	bool operator==(Vector3f vc) {
		return this->x == vc.x && this->y == vc.y && this->z == vc.z;
	}

	bool operator>=(Vector3f vc) {
		return this->x >= vc.x && this->y >= vc.y && this->z >= vc.z;
	}
	bool operator<=(Vector3f vc) {
		return this->x <= vc.x && this->y <= vc.y && this->z <= vc.z;
	}

	bool operator>(Vector3f vc) {
		return this->x > vc.x && this->y > vc.y && this->z > vc.z;
	}
	bool operator<(Vector3f vc) {
		return this->x < vc.x && this->y < vc.y && this->z < vc.z;
	}

	//double& operator[](int index) const {
	//	switch (index) {
	//	case 0:
	//		return this->x;
	//	case 1:
	//		return this->y;
	//	case 2:
	//		return this->z;

	//	}
	//}

	bool contain(Vector3f vc) {
		return Min(vc, Vector3f(this->x,this->y,this->z))==vc;
	}

	static Vector3f Min(const Vector3f& p1, const Vector3f& p2) {
		return Vector3f(std::min(p1.x, p2.x), std::min(p1.y, p2.y),
			std::min(p1.z, p2.z));
	}

	static Vector3f Max(const Vector3f& p1, const Vector3f& p2) {
		return Vector3f(std::max(p1.x, p2.x), std::max(p1.y, p2.y),
			std::max(p1.z, p2.z));
	}
};
//inline double Vector3f::operator[](int index) const {
//	return (&x)[index];
//}


//class Vector2f
//{
//public:
//	Vector2f() : x(0), y(0) {}
//	Vector2f(float xx) : x(xx), y(xx) {}
//	Vector2f(float xx, float yy) : x(xx), y(yy) {}
//	Vector2f operator * (const float& r) const { return Vector2f(x * r, y * r); }
//	Vector2f operator + (const Vector2f& v) const { return Vector2f(x + v.x, y + v.y); }
//	float x, y;
//};

inline Vector3f lerp(const Vector3f& a, const Vector3f& b, const float& t)
{
	return a * (1 - t) + b * t;
}

inline Vector3f normalize(const Vector3f& v)
{
	float mag2 = v.x * v.x + v.y * v.y + v.z * v.z;
	if (mag2 > 0) {
		float invMag = 1 / sqrtf(mag2);
		return Vector3f(v.x * invMag, v.y * invMag, v.z * invMag);
	}

	return v;
}

inline float dotProduct(const Vector3f& a, const Vector3f& b)
{
	return a.x * b.x + a.y * b.y + a.z * b.z;
}

inline Vector3f crossProduct(const Vector3f& a, const Vector3f& b)
{
	return Vector3f(
		a.y * b.z - a.z * b.y,
		a.z * b.x - a.x * b.z,
		a.x * b.y - a.y * b.x
	);
}